package com.easy.mongodb.starter.service.impl;

import com.easy.mongodb.common.enums.ProcessIndexStrategyEnum;
import com.easy.mongodb.common.utils.LogUtils;
import com.easy.mongodb.core.biz.TableInfo;
import com.easy.mongodb.core.toolkit.IndexUtils;
import com.easy.mongodb.core.toolkit.TableInfoHelper;
import com.easy.mongodb.starter.service.AutoProcessIndexService;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.IndexModel;
import org.bson.Document;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static java.util.Arrays.asList;


/**
 * 自动平滑托管索引实现类,本框架默认模式,过程零停机,数据会自动转移至新索引
 * <p>
 * Copyright © 2022 xpc1024 All Rights Reserved
 **/
@Service
@ConditionalOnClass(MongoClient.class)
@ConditionalOnProperty(prefix = "easy-mongodb", name = {"enable"}, havingValue = "true", matchIfMissing = true)
public class AutoProcessIndexServiceImpl implements AutoProcessIndexService {
    @Override
    public Integer getStrategyType() {
        return ProcessIndexStrategyEnum.SMOOTHLY.getStrategyType();
    }

    @Override
    public void processIndexAsync(Class<?> entityClass, MongoClient client) {
        LogUtils.info("===> auto process index mode activated");
        IndexUtils.supplyAsync(this::process, entityClass, client.getDatabase(TableInfoHelper.getTableInfo(entityClass).getDatabase()));
    }


    private synchronized boolean process(Class<?> entityClass, MongoDatabase client) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
        Map<String, IndexModel> entityIndexes = tableInfo.getIndexOps();
        if (entityIndexes.size() == 0) {
            return true;
        }
        Map<String, Document> collectionIndexes = IndexUtils.getIndexs(client, tableInfo);
        Set es = entityIndexes.entrySet();
        Iterator it = es.iterator();
        while (it.hasNext()) {
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            IndexModel v = (IndexModel) entry.getValue();
            if (collectionIndexes.containsKey(k)) {
                boolean isIndexNeedChange = IndexUtils.isIndexNeedChange(collectionIndexes.get(k), v);
                if (isIndexNeedChange) {
                    doUpdateIndex(tableInfo, client, k, v);
                }
            } else {
                doCreateIndex(tableInfo, client, v);
            }
        }
        return true;
    }


    private boolean doUpdateIndex(TableInfo entityInfo, MongoDatabase client, String k, IndexModel v) {
        client.getCollection(entityInfo.getCollectionName()).dropIndex(k);
        client.getCollection(entityInfo.getCollectionName()).createIndexes(asList(v));
        // done.
        return Boolean.TRUE;
    }


    private boolean doCreateIndex(TableInfo entityInfo, MongoDatabase client, IndexModel v) {
        client.getCollection(entityInfo.getCollectionName()).createIndexes(asList(v));
        return Boolean.TRUE;
    }

}
